/****************************************************************************
 * Copyright (C) 2025 Chenyz. All rights reserved.
 *
 * @file    TinyOS_Mutex.c
 *
 * @par     dependencies
 *          - TinyOS_Mutex.h (Mutex management definitions)
 *          - TinyOS_Event.h (Event management definitions)
 *          - TinyOS_Task.h (Task management definitions)
 *
 * @author  Chenyz
 *
 * @brief   Mutex management module for TinyOS with following features:
 *          - Priority inheritance protocol for priority inversion avoidance
 *          - Recursive locking support with lock counting
 *          - Task blocking and waking on mutex operations
 *          - Timeout support for mutex acquisition
 *          - Non-blocking mutex acquisition option
 *          - Critical section protection for thread-safe operations
 *          - Mutex information retrieval and deletion
 *          - Integration with event and task management systems
 *          - Cross-platform compatibility (bare-metal/RTOS systems)
 *
 * @version V1.0
 * @date    2025-05-23
 *
 * @note    Coding standard:
 *          - 4 spaces per indent level
 *          - Follow MISRA-C 2012 guidelines
 *          - Compiler compatibility: C99 and above
 *
 ****************************************************************************/

#include "TinyOS_Mutex.h"
#include "TinyOS_Event.h"
#include "TinyOS_Task.h"

#if TINYOS_ENABLE_MUTEX == 1

/**
 * @brief Initializes a mutex control block
 * 
 * This function sets up a mutex control block with initial unlocked state.
 * The mutex is integrated with the event system for task synchronization
 * and supports priority inheritance to prevent priority inversion.
 * 
 * @param Mutex Pointer to the mutex structure to be initialized
 */
void TinyOS_MutexInit(TinyOS_MutexType* Mutex)
{
    TinyOS_EventBlockInit(&(Mutex->EventBlock), TinyOS_EventTypeMutex);
    Mutex->LockCount = 0;
    Mutex->OwnerTask = (TinyOS_TaskType*)(0);
    Mutex->OwnerTaskPriority = TINYOS_TASK_PRIORITY_COUNT;
}
           
/**
 * @brief Acquires a mutex with optional timeout
 * 
 * This function attempts to acquire the mutex. If the mutex is available,
 * it is locked and the function returns immediately. If the mutex is already
 * locked by another task, the current task will block until the mutex is
 * available or the timeout expires. Supports recursive locking.
 * 
 * @param Mutex Pointer to the mutex structure
 * @param WaitTicks Maximum time to wait for mutex (0 = no waiting)
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MutexTake(TinyOS_MutexType* Mutex, uint32_t WaitTicks)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    /* Check if mutex is available */
    if(Mutex->LockCount == 0)
    {
        /* Mutex available - acquire it */
        Mutex->OwnerTask = CurrentTask;
        Mutex->OwnerTaskPriority = CurrentTask->TaskPrioity;
        Mutex->LockCount++;

        TinyOS_TaskExitCritical(Stauts);
        return TinyOS_ErrorType_NoError;
    }
    else
    {
        /* Check if current task already owns the mutex (recursive lock) */
        if(Mutex->OwnerTask == CurrentTask)
        {
            /* Recursive lock - increment lock count */
            Mutex->LockCount++;
            TinyOS_TaskExitCritical(Stauts);
            return TinyOS_ErrorType_NoError;
        }
        else
        {
            /* Mutex owned by another task - apply priority inheritance */
            if(CurrentTask->TaskPrioity < Mutex->OwnerTask->TaskPrioity)
            {
                TinyOS_TaskType *OwnerTask = Mutex->OwnerTask;
                /* Boost owner task's priority to prevent priority inversion */
                if(OwnerTask->TaskStatus == TINYOS_TASK_READY)
                {
                    TinyOS_TaskSchedulerUnReady(OwnerTask);
                    OwnerTask->TaskPrioity = CurrentTask->TaskPrioity;
                    TinyOS_TaskSchedulerReady(OwnerTask);
                }
                else
                {
                    OwnerTask->TaskPrioity = CurrentTask->TaskPrioity;
                }
            }
            
            /* Put current task into waiting state for mutex */
            TinyOS_EventWait(
                &(Mutex->EventBlock),
                CurrentTask,
                (void*)0,
                TinyOS_EventTypeMutex,
                WaitTicks
            );
            TinyOS_TaskExitCritical(Stauts);                        
            
            /* Schedule another task while waiting */
            TinyOS_TaskScheduler();
            return CurrentTask->TaskEventWaitResult;       
        }
    }
}

/**
 * @brief Attempts to acquire a mutex without blocking
 * 
 * This function attempts to acquire the mutex without blocking the calling task.
 * If the mutex is available or already owned by the current task, it is acquired
 * immediately. If not, the function returns an error without waiting.
 * 
 * @param Mutex Pointer to the mutex structure
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MutexNoWaitTake(TinyOS_MutexType* Mutex)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    /* Check if mutex is available */
    if(Mutex->LockCount == 0)
    {
        /* Mutex available - acquire it */
        Mutex->OwnerTask = CurrentTask;
        Mutex->OwnerTaskPriority = CurrentTask->TaskPrioity;
        Mutex->LockCount++;

        TinyOS_TaskExitCritical(Stauts);
        return TinyOS_ErrorType_NoError;
    }    
    else
    {
        /* Check if current task already owns the mutex (recursive lock) */
        if(Mutex->OwnerTask == CurrentTask)
        {
            /* Recursive lock - increment lock count */
            Mutex->LockCount++;
            TinyOS_TaskExitCritical(Stauts);
            return TinyOS_ErrorType_NoError;
        }    
    }
    
    /* Mutex not available - return error without blocking */
    TinyOS_TaskExitCritical(Stauts);
    return TinyOS_ErrorType_SourceUnavailable;
}

/**
 * @brief Releases a mutex
 * 
 * This function releases a previously acquired mutex. If the mutex was acquired
 * recursively, only the lock count is decremented. When the lock count reaches
 * zero, the mutex is fully released and the next waiting task (if any) is woken.
 * Priority inheritance is reversed when the mutex is fully released.
 * 
 * @param Mutex Pointer to the mutex structure
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MutexGive(TinyOS_MutexType* Mutex)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    TinyOS_TaskType *OwnerTask = Mutex->OwnerTask;
    
    /* Validate mutex state and ownership */
    if(Mutex->LockCount == 0)
    {
        /* Mutex already unlocked - nothing to do */
        TinyOS_TaskExitCritical(Stauts); 
        return TinyOS_ErrorType_NoError;
    }
    if(Mutex->OwnerTask != CurrentTask)
    {
        /* Mutex not owned by current task - cannot release */
        TinyOS_TaskExitCritical(Stauts); 
        return TinyOS_ErrorType_NoError;
    }
    
    /* Decrement recursive lock count */
    Mutex->LockCount--;
    
    /* If still locked (recursive case), return immediately */
    if(Mutex->LockCount > 0)
    {
        TinyOS_TaskExitCritical(Stauts); 
        return TinyOS_ErrorType_NoError;
    }
    
    /* Restore original priority if it was boosted */
    if(OwnerTask->TaskPrioity != Mutex->OwnerTaskPriority)
    {
        if(OwnerTask->TaskStatus == TINYOS_TASK_READY)
        {
            TinyOS_TaskSchedulerUnReady(OwnerTask);
            OwnerTask->TaskPrioity = Mutex->OwnerTaskPriority;
            TinyOS_TaskSchedulerReady(OwnerTask);
        }
        else
        {
            OwnerTask->TaskPrioity = Mutex->OwnerTaskPriority;
        }
    }
        
    /* Check if any tasks are waiting for this mutex */
    if(TinyOS_EventWaitCount(&Mutex->EventBlock) > 0)
    {
        /* Wake the highest priority waiting task */
        TinyOS_TaskType* TempTaskHandler = TinyOS_EventWake(
            &(Mutex->EventBlock),
            (void*)0,
            TinyOS_ErrorType_NoError
        );         
        
        /* Transfer ownership to the woken task */
        Mutex->OwnerTask = TempTaskHandler;
        Mutex->OwnerTaskPriority = TempTaskHandler->TaskPrioity;
        Mutex->LockCount++;   
                   
        /* Schedule immediately if woken task has higher priority */
        if(TempTaskHandler->TaskPrioity < CurrentTask->TaskPrioity)
        {
            TinyOS_TaskScheduler();          
        }                
    }
    TinyOS_TaskExitCritical(Stauts); 
    return TinyOS_ErrorType_NoError;       
}

/**
 * @brief Deletes a mutex and notifies all waiting tasks
 * 
 * This function deletes a mutex, waking up all tasks that were waiting for it
 * with a deletion notification. If the mutex was locked, the owner's priority
 * is restored before deletion.
 * 
 * @param Mutex Pointer to the mutex structure to be deleted
 * @return uint32_t Error code indicating operation result
 */
uint32_t TinyOS_MutexDelete(TinyOS_MutexType* Mutex)
{
    uint32_t WakeTaskCount;
    uint32_t Stauts = TinyOS_TaskEnterCritical();
    
    /* Only process if mutex is currently locked */
    if(Mutex->LockCount > 0)
    {
        /* Restore owner's original priority if it was boosted */
        if(Mutex->OwnerTaskPriority != Mutex->OwnerTask->TaskPrioity)
        {
            if(Mutex->OwnerTask->TaskStatus == TINYOS_TASK_READY)
            {
                TinyOS_TaskSchedulerUnReady(Mutex->OwnerTask);
                Mutex->OwnerTask->TaskPrioity = Mutex->OwnerTaskPriority;
                TinyOS_TaskSchedulerReady(Mutex->OwnerTask);
            }
            else
            {
                Mutex->OwnerTask->TaskPrioity = Mutex->OwnerTaskPriority;
            }
        }
        
        /* Wake all tasks waiting on this mutex with deletion notification */
        WakeTaskCount = TinyOS_EventRemoveAll(
            &(Mutex->EventBlock),
            (void*)0,
            TinyOS_ErrorType_Delete
        );  
        
        /* Schedule tasks if any were woken */
        if(WakeTaskCount > 0) 
        {
            TinyOS_TaskScheduler();
        }            
    }            
    TinyOS_TaskExitCritical(Stauts); 
    return TinyOS_ErrorType_NoError;        
}

/**
 * @brief Retrieves information about a mutex
 * 
 * This function returns the current state of the mutex including its
 * lock count, owner information, priority inheritance status, and
 * number of tasks waiting for the mutex.
 * 
 * @param Mutex Pointer to the mutex structure
 * @param MuetxInfo Pointer to a structure where the mutex information will be stored
 */
void TinyOS_MutexGetInfo(
    TinyOS_MutexType* Mutex, 
    TinyOS_MutexInfoType* MuetxInfo
)
{
    uint32_t Stauts = TinyOS_TaskEnterCritical();

    MuetxInfo->TaskCount = TinyOS_EventWaitCount(&Mutex->EventBlock);
    MuetxInfo->OwnerTaskPriority = Mutex->OwnerTaskPriority;
    
    /* Get current priority (may be different due to priority inheritance) */
    if (Mutex->OwnerTask != (TinyOS_TaskType *)0)
    {
        MuetxInfo->InheritedPriority = Mutex->OwnerTask->TaskPrioity;
    }
    else
    {
        MuetxInfo->InheritedPriority = TINYOS_TASK_PRIORITY_COUNT;
    }
    
    /* Copy owner task and lock count information */
    MuetxInfo->OwnerTask = Mutex->OwnerTask;
    MuetxInfo->LockCount = Mutex->LockCount;

    TinyOS_TaskExitCritical(Stauts);    
}


#endif

